home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FM Towns: Free Software Collection 10
/
FM Towns Free Software Collection 10.iso
/
ms_dos
/
tool
/
expr
/
expr.c
next >
Wrap
Text File
|
1995-01-29
|
13KB
|
588 lines
/*
VenusScript 式解析ルーチン Version 1.00a
Copyleft 1994 Delmonta
このソースプログラムに関する著作権の行使を留保します。
コンパイル方法:
・コンパイルオプションでマクロ「STANDALONE」を定義してください。
・小数対応にするには、コンパイルオプションでマクロ「USE_FLOAT」を
定義してください。
*/
#include<stdio.h>
#include<stdlib.h>
#include<errno.h> /* errno */
#include<ctype.h>
#include<string.h>
#include<signal.h> /* signal() */
#include<float.h> /* FPE_INTDIV0など */
#ifndef STANDALONE
#include"venus.h"
#else
#ifdef USE_FLOAT
typedef double CONST_T;
#else
typedef long CONST_T;
#endif
#define STRCMP stricmp /* これをstrcmpに変えれば */
/* 変数名の大文字・小文字を */
/* 厳密に区別する */
#define ISALPHA(c) (isalpha(c) || (c)=='_')
#define ISALNUM(c) (isalnum(c) || (c)=='_')
#define MEMBERSOF(array) (sizeof(array)/sizeof((array)[0]))
void errexit(char *,...);
#endif
/*
演算子番号は、次のビット構成を持つ。
Bit15 : 1ならば右結合
Bit14 : 1ならば単項演算子
Bit11-4 : 優先順位
Bit3-0 : 演算子個別の識別番号
*/
#define OP_ENDOFSTACK 0xffff /* 演算子スタックの終了を示す特別な値 */
#define OP_LPARE 0xfffe /* 左括弧 ( */
#define OP_RPARE 0xfffd /* 右括弧 ) */
#define OP_LNOT 0xc0f0 /* 論理否定 ! */
#define OP_NOT 0xc0f1 /* ビット反転 ~ */
#define OP_POW 0x80e0 /* 累乗 ** */
#define OP_SMINUS 0xc0d0 /* 負の符号 - */
#define OP_SPLUS 0xc0d1 /* 正の符号 + */
#define OP_MULT 0x00c0 /* 乗算 * */
#define OP_DIV 0x00c1 /* 除算 / */
#define OP_MOD 0x00c2 /* 剰余 % */
#define OP_LSHIFT 0x00b0 /* 左シフト << */
#define OP_RSHIFT 0x00b1 /* 右シフト >> */
#define OP_PLUS 0x00a0 /* 加算 + */
#define OP_MINUS 0x00a1 /* 減算 - */
#define OP_AND 0x0090 /* ビットAND & */
#define OP_XOR 0x0091 /* ビットXOR ^ */
#define OP_OR 0x0092 /* ビットOR | */
#define OP_LESS 0x0080 /* 比較 < */
#define OP_ELESS 0x0081 /* <= */
#define OP_GREAT 0x0082 /* > */
#define OP_EGREAT 0x0083 /* >= */
#define OP_EQUAL 0x0084 /* == */
#define OP_NEQUAL 0x0085 /* != */
#define OP_LET 0x8070 /* 代入 = */
#define OP_LAND 0x0060 /* 論理AND && */
#define OP_LOR 0x0061 /* 論理OR || */
#define OPLEVEL(op) ((op) & 0x0ff0)
#define ISRIGHT(op) ((op) & 0x8000)
#define ISSINGLE(op) ((op) & 0x4000)
/*-----------------------------------型--------------------------------------*/
typedef int OPERATOR_T; /* 演算子 */
typedef int VARNO_T; /* 変数番号 */
typedef struct
{
enum {CONSTANT,VARIABLE,OPERATOR,ENDOFSTRING} type;
union
{ CONST_T constant; /* 定数 */
int varno; /* 変数 */
OPERATOR_T op; /* 演算子 */
} data;
} TOKEN_T;
static TOKEN_T Token;
static struct VAR_T
{
CONST_T value;
char *name;
} Variable[256];
static struct VSTACK
{
union {CONST_T constant; VARNO_T varno;} value;
enum {VSTK_VAR,VSTK_CONST} type;
} Vstack[256];
static OPERATOR_T Ostack[256];
static int Vstackp;
static int Ostackp;
static char *Expr;
/*-----------------------------初期化とエラー処理----------------------------*/
static void initexpr(char *s)
{
Token.type = ENDOFSTRING;
Vstackp = Ostackp = 0;
Expr = s;
}
void fperr(int a,int b)
{
switch (b)
{
case FPE_INTDIV0:
case FPE_ZERODIVIDE:
errexit("0で割りました");
case FPE_OVERFLOW:
errexit("オーバーフローしました");
case FPE_UNDERFLOW:
errexit("アンダーフローしました");
case FPE_INVALID:
errexit("不正な演算を実行しました");
}
signal(SIGFPE,fperr); /* デノーマル数などは問題にしない */
}
/*---------------------------スタックフレームの管理--------------------------*/
static OPERATOR_T popop(void)
{
if (Ostackp==0)
return OP_ENDOFSTACK;
else
return Ostack[--Ostackp];
}
static void pushop(OPERATOR_T op)
{
if (op==OP_ENDOFSTACK)
return;
else if (Ostackp==MEMBERSOF(Ostack))
errexit("演算子スタックオーバーフロー.");
else
Ostack[Ostackp++] = op;
}
static char Vstack_overflow[] = "オペランドスタックオーバーフロー.";
static void pushconst(CONST_T val)
{
if (Vstackp==MEMBERSOF(Vstack))
errexit(Vstack_overflow);
else
{
Vstack[Vstackp].type = VSTK_CONST;
Vstack[Vstackp++].value.constant = val;
}
}
static void pushvar(VARNO_T varno)
{
if (Vstackp==MEMBERSOF(Vstack))
errexit(Vstack_overflow);
else
{
Vstack[Vstackp].type = VSTK_VAR;
Vstack[Vstackp++].value.varno = varno;
}
}
static char Vstack_underflow[] = "オペランドが足りません.";
static CONST_T popconst(void)
{
if (Vstackp==0)
errexit(Vstack_underflow);
else if (Vstack[--Vstackp].type==VSTK_CONST)
return Vstack[Vstackp].value.constant;
else
return Variable[Vstack[Vstackp].value.varno].value;
}
static VARNO_T popvar(void)
{
if (Vstackp==0)
errexit(Vstack_underflow);
else if (Vstack[--Vstackp].type==VSTK_CONST)
errexit("変数が要求されるところに右辺式があります.");
else
return Vstack[Vstackp].value.varno;
}
/*-----------------------------------計算------------------------------------*/
#ifdef USE_FLOAT
#include<math.h>
static CONST_T POW(CONST_T x,CONST_T y)
{
if (x==0) /* y<=0でも0**y==1 */
return 1;
else if (x<0 && y!=ceil(y))
errexit("負の数に対する指数が整数になっていません");
else
return pow(x,y);
}
#else
static CONST_T POW(CONST_T x,CONST_T y)
{
if (y==0) /* y<=0でも0**y==1 */
return 1;
else if (y<0)
return 0; /* 切り捨てられて結果は0 */
else
{
CONST_T z = x;
while (--y)
z *= x;
return z;
}
}
#endif
/*-------------------------------演算子の処理--------------------------------*/
static void getoperator(void)
{
int flag = Token.type==ENDOFSTRING ||
(Token.type==OPERATOR && Token.data.op!=OP_RPARE);
/* 直前のトークンは2項演算子の前に来ないものか */
switch (*Expr)
{
case '(': Token.data.op = OP_LPARE; break;
case ')': Token.data.op = OP_RPARE; break;
case '!':
if (*(Expr+1)=='=')
Expr++,Token.data.op = OP_NEQUAL;
else
Token.data.op = OP_LNOT;
break;
case '~': Token.data.op = OP_NOT; break;
case '*':
if (*(Expr+1)=='*')
Expr++,Token.data.op = OP_POW;
else
Token.data.op = OP_MULT;
break;
case '+':
if (flag)
Token.data.op = OP_SPLUS;
else
Token.data.op = OP_PLUS;
break;
case '-':
if (flag)
Token.data.op = OP_SMINUS;
else
Token.data.op = OP_MINUS;
break;
case '/': Token.data.op = OP_DIV; break;
case '%': Token.data.op = OP_MOD; break;
case '<':
if (*(Expr+1)=='<')
Expr++,Token.data.op = OP_LSHIFT;
else if (*(Expr+1)=='=')
Expr++,Token.data.op = OP_ELESS;
else
Token.data.op = OP_LESS;
break;
case '>':
if (*(Expr+1)=='>')
Expr++,Token.data.op = OP_RSHIFT;
else if (*(Expr+1)=='=')
Expr++,Token.data.op = OP_EGREAT;
else
Token.data.op = OP_GREAT;
break;
case '&':
if (*(Expr+1)=='&')
Expr++,Token.data.op = OP_LAND;
else
Token.data.op = OP_AND;
break;
case '|':
if (*(Expr+1)=='|')
Expr++,Token.data.op = OP_LOR;
else
Token.data.op = OP_OR;
break;
case '^': Token.data.op = OP_XOR;
case '=':
if (*(Expr+1)=='=')
Expr++,Token.data.op = OP_EQUAL;
else
Token.data.op = OP_LET;
break;
default:
errexit("不正な演算子です:%c",*Expr);
};
if (flag && !ISSINGLE(Token.data.op))
errexit("2つの演算子が連続しています");
Expr++;
Token.type = OPERATOR;
}
static void evalop(OPERATOR_T op)
{
CONST_T a;
switch (op)
{
case OP_LPARE:
errexit("'('に対応する')'がありません");
case OP_LNOT: a = !popconst(); break;
case OP_NOT: a = ~(long)popconst(); break;
case OP_POW: a = popconst(); a = POW(popconst(),a); break;
case OP_SPLUS: a = popconst(); break;
case OP_SMINUS: a = -popconst(); break;
case OP_MULT: a = popconst() * popconst(); break;
case OP_DIV: a = popconst(); a = popconst()/a; break;
case OP_MOD:
a = popconst();
#ifndef USE_FLOAT
a = popconst() % a;
#else
a = fmod(popconst(),a);
#endif
break;
case OP_PLUS: a = popconst() + popconst(); break;
case OP_MINUS: a = popconst(); a = popconst() - a; break;
case OP_LSHIFT:
a = popconst();
#ifndef USE_FLOAT
a = popconst() << a;
#else
a = ldexp(popconst(),a);
#endif
break;
case OP_RSHIFT:
a = popconst();
#ifndef USE_FLOAT
a = popconst() >> a;
#else
a = ldexp(popconst(),-a);
#endif
break;
/* OP_LANDとOP_LORで一度aに値を設定しているのは */
/* Cの&&あるいは||では第1項だけで値が確定すれば */
/* 第2項は評価しないため */
case OP_AND: a =(long)popconst() & (long)popconst(); break;
case OP_XOR: a =(long)popconst() ^ (long)popconst(); break;
case OP_OR: a =(long)popconst() | (long)popconst(); break;
case OP_LAND: a = popconst(); a = popconst() && a; break;
case OP_LOR: a = popconst(); a = popconst() || a; break;
case OP_LESS: a = popconst(); a = popconst() < a; break;
case OP_ELESS: a = popconst(); a = popconst() <= a; break;
case OP_GREAT: a = popconst(); a = popconst() > a; break;
case OP_EGREAT: a = popconst(); a = popconst() >= a; break;
case OP_EQUAL: a = popconst() == popconst(); break;
case OP_NEQUAL: a = popconst() != popconst(); break;
case OP_LET:
{
VARNO_T b;
a = popconst();
b = popvar();
Variable[b].value = a;
}
break;
default:
errexit("不正な演算子です:演算子ID 0x%04X",op);
}
pushconst(a);
}
/*----------------------------トークンの切り出し-----------------------------*/
static char Couple_values[] = "2つの定数/変数が連続して表記されています";
static void getvariable(void)
{
static VARNO_T varnum = 0; /* 変数の総数 */
char *p = Expr;
char c;
VARNO_T i;
if (Token.type==CONSTANT || Token.type==VARIABLE)
errexit(Couple_values);
while (ISALNUM(*p))
p++;
c = *p;
*p = '\0';
for (i=0 ; i<varnum ; i++)
{
if (STRCMP(Expr,Variable[i].name)==0)
goto end;
}
varnum++;
if (i==MEMBERSOF(Variable))
errexit("変数は最大で%d個までです",i);
if ((Variable[i].name=strdup(Expr))==NULL)
errexit("メモリ不足です");
Variable[i].value = 0;
end:
Token.type = VARIABLE;
Token.data.varno = i;
Expr = p;
*p = c;
}
static int expr_gettoken(void)
{
while (isspace(*Expr))
Expr++;
if (*Expr=='\0') /* 文字列の終わり */
return 0;
/* 定数か? */
if (isdigit(*Expr) || *Expr=='.')
{ /* ピリオドとの比較は整数に限定する場合は意味がない */
CONST_T val;
if (Token.type==CONSTANT || Token.type==VARIABLE)
errexit(Couple_values);
errno = 0;
if (*Expr=='0' && (Expr[1]=='x' || Expr[1]=='X'))
val = (long)strtoul(Expr+2,&Expr,16);
else
{
#ifdef USE_FLOAT
val = strtod(Expr,&Expr);
#else
val = strtoul(Expr,&Expr,10);
#endif
}
if (errno==ERANGE && val!=0)
errexit("定数の値が大きすぎます.");
Token.data.constant = val;
Token.type = CONSTANT;
}
else if (ISALPHA(*Expr))
getvariable();
else
getoperator();
return 1;
}
/*------------------------------メインルーチン-------------------------------*/
extern CONST_T evalexpr(char *s)
{
OPERATOR_T op;
int lev1,lev2;
initexpr(s);
while (expr_gettoken())
{
switch (Token.type)
{
case CONSTANT:
pushconst(Token.data.constant);
break;
case VARIABLE:
pushvar(Token.data.varno);
break;
case OPERATOR:
if (Token.data.op==OP_LPARE)
{
pushop(OP_LPARE);
break;
}
if (Token.data.op==OP_RPARE)
{
while ((op=popop())!=OP_LPARE)
{
if (op==OP_ENDOFSTACK)
errexit("')'に対応する'('がありません.");
evalop(op);
}
break;
}
op = popop();
lev1 = OPLEVEL(op);
lev2 = OPLEVEL(Token.data.op);
if (op==OP_LPARE || op==OP_ENDOFSTACK ||
ISSINGLE(Token.data.op) ||
lev1<lev2 || (lev1==lev2 && ISRIGHT(op)) )
pushop(op);
else
evalop(op);
pushop(Token.data.op);
break;
}
}
while ((op=popop())!=OP_ENDOFSTACK)
evalop(op);
if (Vstackp!=1)
printf("Vstackp==%d\n",Vstackp);
return popconst();
}
/*---------------スタンドアローンの計算ツールとして使用する場合--------------*/
#ifdef STANDALONE
#include<setjmp.h>
#include<stdarg.h>
jmp_buf Jmp_buf;
void errexit(char *s,...)
{
va_list ap;
va_start(ap,s);
vprintf(s,ap);
va_end(ap);
putchar('\a');
putchar('\n');
longjmp(Jmp_buf,1);
}
int main(int argc,char **argv)
{
char buf[256];
#ifdef USE_FLOAT
#define VER "(小数対応バージョン)"
#else
#define VER "(整数限定バージョン)"
#endif
printf( "式計算ユーティリティ" VER " Version 1.00a\n"
"Copyleft 1994 Delmonta\n");
#undef VER
setjmp(Jmp_buf);
signal(SIGFPE,fperr);
while (printf(">"),fgets(buf,sizeof(buf),stdin)&&*buf!='\n')
{
#ifdef USE_FLOAT
printf("%.*g\n",DBL_DIG+1,evalexpr(buf));
/* i80x87のdoubleの精度は10進で15.95 */
/* 桁だがDBL_DIG=15と定義されている */
#else
printf("%ld\n",evalexpr(buf));
#endif
}
}
#endif